/**
 * 
 */
package net.kaizhong.excel.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.RichTextString;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.util.CollectionUtils;

import net.kaizhong.excel.Converter;
import net.kaizhong.excel.Excel;
import net.kaizhong.excel.ExcelOperateService;

/**
 * @author huqifeng
 *
 */
public class ExcelOperateServiceImpl<T> implements ExcelOperateService<T> {

    private static Log log = LogFactory.getLog(ExcelOperateServiceImpl.class);

    /**
     * 导入或导出的java
     */
    private T pojoClass = null;

    /**
     * 配置Bean中导出字段的获取顺序 Map<order,Bean属性>
     */
    private Map<Integer, String> fieldNameMap = new TreeMap<Integer, String>();

    /**
     * 配置每个字段导出的标题 从注解 Excel.exportName()获取标题,从 Excel.order() 获取字段序号
     * Map<bean属性,excel标题>
     */
    private Map<String, String> fieldTitleMap = new HashMap<String, String>();

    /**
     * 配置每个导出字段的列宽 Map<bean属性,excel列宽>
     */
    private Map<String, Integer> fieldWidthMap = new HashMap<String, Integer>();

    /**
     * 配置没有导出字段的取值方法 Map<bean属性,属性的get方法>
     */
    private Map<String, Method> fieldGetMethodMap = new HashMap<String, Method>();

    /**
     * 配置没有导出字段的取值方法 Map<bean属性,属性的set方法>
     */
    private Map<String, Method> fieldSetMethodMap = new HashMap<String, Method>();

    /**
     * 配置每个字段导出时的转换实现类 Map<bean属性,属性转换实现类>
     */
    private Map<String, String> fieldDataCvtMap = new HashMap<String, String>();

    /**
     * 配置每个字段导出的标题 从注解 Excel.exportName()获取标题 Map<excel标题,bean属性>
     */
    private Map<String, String> titleFieldMap = new HashMap<String, String>();

    /**
     * 
     */
    public ExcelOperateServiceImpl() {

    }

    /**
     * 通用导出数据到excel文件
     * 
     * @param sheetName
     * @param dataList
     * @param out
     * @throws Exception
     */
    @Override
    public void exportExcel(String sheetName, List<T> dataList, OutputStream out) throws Exception {
        // 导出数据列表为空则不做处理
        if (CollectionUtils.isEmpty(dataList)) {
            log.info("没有需要导出到excel的数据");
            return;
        }

        if (dataList.size() > MAX_DATA_SIZE) {
            log.error("一次导出的数据量过大,请检查数据查询条件");
            throw new Exception("传入数据量过大");
        }

        if (StringUtils.isBlank(sheetName) || null == out) {
            log.error("参数传入非法,请检查代码");
            throw new Exception("传入参数不合法");
        }

        // 声明一个工作薄
        Workbook workbook = new HSSFWorkbook();
        // 生成一个表格
        Sheet sheet = workbook.createSheet(sheetName);

        // 得到对应java对象定义的所有字段
        Class<? extends Object> clazz = getPojoClass(dataList);
        initBeanInfoByClass(clazz);

        int rowIndex = 0;
        int cellIndex = 0;
        // 产生表格标题行
        Row row = sheet.createRow(rowIndex);
        for (Map.Entry<Integer, String> entry : fieldNameMap.entrySet()) {
            Cell cell = row.createCell(cellIndex);
            RichTextString text = new HSSFRichTextString(fieldTitleMap.get(entry.getValue()));
            cell.setCellValue(text);
            if (null != fieldWidthMap.get(entry.getValue())) {
                sheet.setColumnWidth(entry.getKey(), 256 * fieldWidthMap.get(entry.getValue()));
            }
            cellIndex++;
        }

        for (T pojo : dataList) {
            rowIndex++;
            row = sheet.createRow(rowIndex);
            cellIndex = 0;
            for (Map.Entry<Integer, String> entry : fieldNameMap.entrySet()) {
                Cell cell = row.createCell(cellIndex);
                Method getMethod = fieldGetMethodMap.get(entry.getValue());
                Object value = "";
                if (null != getMethod) {
                    value = getMethod.invoke(pojo, new Object[] {});
                }

                // 有配置转换实现类则进行调用处理
                if (null != fieldDataCvtMap.get(entry.getValue())) {

                    try {
                        Converter cvt = (Converter) Class.forName(fieldDataCvtMap.get(entry.getValue())).newInstance();
                        value = cvt.convertOut(value);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                // 对excel安全赋值
                RichTextString text = new HSSFRichTextString(String.valueOf(value));
                cell.setCellValue(text);
                cellIndex++;
            }
        }

        workbook.write(out);
    }

    /**
     * 
     * @param dataList
     * @return
     */
    private Class<? extends Object> getPojoClass(List<T> dataList) {
        return dataList.get(0).getClass();
    }

    /**
     * 
     * @param clazz
     */
    private void initBeanInfoByClass(Class<?> clazz) {

        if (null == clazz) {
            return;
        }

        // 清空旧信息
        fieldNameMap.clear();
        fieldTitleMap.clear();
        titleFieldMap.clear();
        fieldGetMethodMap.clear();
        fieldSetMethodMap.clear();
        fieldWidthMap.clear();
        fieldDataCvtMap.clear();

        Field[] fileds = clazz.getDeclaredFields();
        // Method[] methods = clazz.getDeclaredMethods();

        if (null != fileds && fileds.length > 0) {
            // 遍历所有字段
            for (int i = 0; i < fileds.length; i++) {
                Field field = fileds[i];

                Excel excel = field.getAnnotation(Excel.class);
                // 如果字段上设置了 Excel 注解
                if (excel != null) {
                    // 没有设置序号使用字段出现的自然顺序
                    int index = excel.order();
                    if (index < 1) {
                        index = i + 1;
                    }
                    // 导出字段的顺序
                    fieldNameMap.put(Integer.valueOf(index), field.getName());

                    // 没有设置字段名称默认使用类定义的成员属性名称
                    String exportName = excel.exportName();
                    if (StringUtils.isBlank(exportName)) {
                        exportName = field.getName();
                    }
                    // 导出字段和excel标题映射
                    fieldTitleMap.put(field.getName(), exportName);
                    titleFieldMap.put(exportName, field.getName());

                    String methodName = generateMethodName(field.getName(), "get");
                    try {
                        Method m = clazz.getMethod(methodName, new Class[] {});
                        fieldGetMethodMap.put(field.getName(), m);
                    } catch (Exception e) {
                        log.error("从目标Bean中获取方法出错,请检查配置;" + e.getMessage());
                        e.printStackTrace();
                    }

                    methodName = generateMethodName(field.getName(), "set");
                    try {
                        Method m = clazz.getMethod(methodName, new Class[] { field.getType() });
                        fieldSetMethodMap.put(field.getName(), m);
                    } catch (Exception e) {
                        log.error("从目标Bean中获取方法出错,请检查配置;" + e.getMessage());
                        e.printStackTrace();
                    }

                    if (excel.fieldWidth() > 0) {
                        fieldWidthMap.put(field.getName(), excel.fieldWidth());
                    }
                    if (StringUtils.isNotBlank(excel.dataConverter())) {
                        fieldDataCvtMap.put(field.getName(), excel.dataConverter());
                    }
                }
            }
        }
    }

    /**
     * 
     * @param fieldName
     * @param type
     * @return
     */
    private String generateMethodName(String fieldName, String type) {
        StringBuffer methodName = new StringBuffer(type);
        if (StringUtils.isNotBlank(fieldName)) {
            methodName.append(fieldName.substring(0, 1).toUpperCase());
            methodName.append(fieldName.substring(1));
        }
        return methodName.toString();
    }

    /**
     * 通用从excel文件导入数据
     * 
     * @param srcFile
     * @return
     * @throws Exception
     */
    @Override
    public List<T> importExcel(File srcFile, Class<T> pojoClass, boolean hasTitle) throws Exception {
        List<T> distList = new ArrayList<T>();

        if (null == srcFile || !srcFile.exists()) {
            log.error("导入文件必须存在,请检查代码");
            throw new Exception("传入参数不合法");
        }

        initBeanInfoByClass(pojoClass);

        // 将传入的File构造为FileInputStream;
        FileInputStream in = new FileInputStream(srcFile);

        // 得到工作表
        HSSFWorkbook book = new HSSFWorkbook(in);
        // 得到第一页
        HSSFSheet sheet = book.getSheetAt(0);
        // 得到第一面的所有行
        Iterator<Row> rowIte = sheet.rowIterator();
        // Map<导入列的序号, 导入列对应Bean的属性名称>
        Map<Integer, String> cellMap = new HashMap<Integer, String>();

        if (hasTitle) {
            // 得到第一行，也就是标题行
            Row titleRow = rowIte.next();

            // 得到第一行的所有列
            Iterator<Cell> cellTitles = titleRow.cellIterator();
            int cellIndex = 0;
            while (cellTitles.hasNext()) {
                Cell cell = cellTitles.next();
                String title = cell.getStringCellValue();
                log.info(titleFieldMap.get(title));
                cellMap.put(cellIndex, titleFieldMap.get(title));
                cellIndex++;
            }
        } else {
            // 不包含标题行的默认使用Bean定义的顺序
            cellMap.putAll(fieldNameMap);
        }

        while (rowIte.hasNext()) {
            // 标题下的第一行
            Row rown = rowIte.next();

            // 行的所有列
            Iterator<Cell> cellIte = rown.cellIterator();

            // 得到传入类的实例
            T pojo = pojoClass.newInstance();

            int k = 0;
            // 遍历一行的列
            while (cellIte.hasNext()) {
                Cell cell = cellIte.next();
                // 这里得到此列的对应的标题
                String fieldName = cellMap.get(k);

                // 如果这一列的标题和类中的某一列的Annotation相同，那么则调用此类的的set方法，进行设值
                if (fieldSetMethodMap.containsKey(fieldName)) {
                    Object value = null;
                    Method setMethod = (Method) fieldSetMethodMap.get(fieldName);

                    if (null != setMethod) {
                        // 如果需要对excel数据做转换则调用转换实现类,直接注入需要的值
                        if (fieldDataCvtMap.containsKey(fieldName)) {
                            try {
                                Converter cvt = (Converter) Class.forName(fieldDataCvtMap.get(fieldName)).newInstance();
                                value = cvt.convertIn(cell.getStringCellValue());
                                setMethod.invoke(pojo, value);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        } else {
                            // 没有转换器则根据Bean的类型参数注入
                            // 得到setter方法的参数
                            Type[] ts = setMethod.getGenericParameterTypes();
                            // 只要一个参数
                            String xclass = ts[0].toString();
                            if (xclass.equals("class java.lang.String")) {
                                setMethod.invoke(pojo, cell.getStringCellValue());
                            } else if (xclass.equals("class java.util.Date")) {
                                setMethod.invoke(pojo, cell.getDateCellValue());
                            } else if (xclass.equals("class java.lang.Boolean")) {
                                setMethod.invoke(pojo, cell.getBooleanCellValue());
                            } else if (xclass.equals("class java.lang.Integer")) {
                                setMethod.invoke(pojo, new Integer(cell.getStringCellValue()));
                            } else if (xclass.equals("class java.lang.Long")) {
                                setMethod.invoke(pojo, new Long(cell.getStringCellValue()));
                            } else if (xclass.equals("class java.lang.Double")) {
                                setMethod.invoke(pojo, new Double(cell.getStringCellValue()));
                            }
                        }
                    }
                }
                k++;
            }

            distList.add(pojo);
        }

        return distList;
    }

    public T getPojoClass() {
        return pojoClass;
    }

    public void setPojoClass(T pojoClass) {
        this.pojoClass = pojoClass;
    }

    public Map<Integer, String> getFieldNameMap() {
        return fieldNameMap;
    }

    public void setFieldNameMap(Map<Integer, String> fieldNameMap) {
        this.fieldNameMap = fieldNameMap;
    }

    public Map<String, String> getFieldTitleMap() {
        return fieldTitleMap;
    }

    public void setFieldTitleMap(Map<String, String> fieldTitleMap) {
        this.fieldTitleMap = fieldTitleMap;
    }

    public Map<String, Integer> getFieldWidthMap() {
        return fieldWidthMap;
    }

    public void setFieldWidthMap(Map<String, Integer> fieldWidthMap) {
        this.fieldWidthMap = fieldWidthMap;
    }

}
